/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.neuronbit.businessflow.intellij.dialog;

import com.intellij.ide.util.treeView.AbstractTreeNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiType;
import com.intellij.psi.impl.java.stubs.index.JavaFullClassNameIndex;
import com.intellij.psi.search.GlobalSearchScope;
import com.mxgraph.model.mxICell;
import com.neuronbit.businessflow.intellij.model.FormattedMethodString;
import com.neuronbit.businessflow.intellij.util.PsiUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import static com.neuronbit.businessflow.intellij.dialog.FieldTreeListChooseDialog.NodeType.FIELD;
import static com.neuronbit.businessflow.intellij.util.PsiUtils.isAtomic;

/**
 * This is dialog box for selecting methods of classes
 */
public class FlowReturnFieldValueSourceTreeListChooseDialog extends FieldTreeListChooseDialog {
    private static final Logger LOG = Logger.getInstance(MethodParamValueSourceTreeChooseDialog.class);
    /**
     * Instantiates a new Flow return field value source choose dialog.
     *
     * @param project      the project
     * @param cell         the cell
     * @param name         the name
     * @param expectedType the expected type
     * @param defaultValue
     */
    public FlowReturnFieldValueSourceTreeListChooseDialog(@Nullable Project project, @NotNull List<mxICell> cell, String name, @NotNull PsiType expectedType, @NotNull String defaultValue) {
        super(project, cell, name, expectedType, defaultValue);
    }

    /**
     * The type My tree node.
     */
    public class MyTreeNode extends FieldTreeNode {

        /**
         * Instantiates a new My tree node.
         *
         * @param project the project
         * @param value   the value
         */
        protected MyTreeNode(Project project, @NotNull NodeValue value) {
            super(project, value);
        }

        @Override
        protected FieldTreeNode createNode(Project project, NodeValue value) {
            return new MyTreeNode(project, value);
        }

        @Override
        public @NotNull Collection<? extends AbstractTreeNode<?>> getChildren() {
            ArrayList<AbstractTreeNode<?>> list = new ArrayList<>();
            NodeType nodeType = this.getValue().getNodeType();
            switch (nodeType) {
                case ROOT -> {
                    extractedFirstTask(getCell(), list);
                }
                case TASK -> {
                    FormattedMethodString method = this.getValue().getMethod();
                    if (StringUtils.isBlank(method.getReturnType())) {
                        return list;
                    }

                    Collection<PsiClass> classes = JavaFullClassNameIndex.getInstance()
                            .getClasses(method.getReturnType(), getProject(), GlobalSearchScope.allScope(getProject()));
                    if (classes.isEmpty()) {
                        LOG.info(String.format("class %s of method return value not find ", method.getReturnType()));
                        return list;
                    }
                    for (PsiClass aClass : classes) {
                        expandTypeRecursively("output", PsiUtils.getPsiTypeFromPsiClass(aClass), list);
                    }
                }
                case OUTPUT -> {
                    Collection<PsiClass> classes = JavaFullClassNameIndex.getInstance()
                            .getClasses(this.getValue().getClazzName().getQualifiedName(), getProject(), GlobalSearchScope.allScope(getProject()));
                    if (classes.isEmpty()) {
                        return list;
                    }
                    for (PsiClass psiClass : classes) {
                        if (isAtomic(psiClass)) {
                            list.add(new MyTreeNode(getProject(), new NodeValue(FIELD, this.getValue().getQualifiedName(), this.getValue().getName(), this.getValue().getClazzName())));
                        } else {
                            Arrays.stream(psiClass.getAllFields())
                                    .forEach(field -> {
                                        expandTypeRecursively(field.getName(), field.getType(), list);
                                    });
                        }
                    }
                }
                case FIELD -> {
                }
            }
            return list;
        }


    }

    @Override
    public @NotNull Object createRootElement() {
        return new MyTreeNode(getProject(), new NodeValue(FlowReturnFieldValueSourceTreeListChooseDialog.NodeType.ROOT, null, "root object", null));
    }
}
